/**
 * Example 2: Manual-Reset Logic with Auto-Reset Event
 *
 * This example demonstrates the problem that occurs when a module using manual-reset event logic
 * actually receives an auto-reset event. The thread explicitly resets an event that has already
 * been automatically reset, and other threads miss the event notification.
 */

#include <Windows.h>
#include <iostream>
#include <string>
#include <vector>

// Simulating two separate modules that use the same named event

// Module A: Creates an auto-reset event (first to start)
HANDLE CreateAutoResetEvent() {
    std::cout << "Module A: Creating auto-reset event..." << std::endl;
    HANDLE hEvent = CreateEvent(
        NULL,     // Default security attributes
        FALSE,    // Auto-reset event
        FALSE,    // Initial state is nonsignaled
        L"Global\\Example2_Event" // Named event
    );
    
    if (hEvent == NULL) {
        std::cerr << "CreateEvent failed with error " << GetLastError() << std::endl;
        return NULL;
    }
    
    DWORD lastError = GetLastError();
    if (lastError == ERROR_ALREADY_EXISTS) {
        std::cout << "Event already exists! Got handle to existing event." << std::endl;
    }
    
    return hEvent;
}

// Module B: Tries to create a manual-reset event (starts second)
HANDLE CreateManualResetEvent() {
    std::cout << "Module B: Creating manual-reset event..." << std::endl;
    HANDLE hEvent = CreateEvent(
        NULL,     // Default security attributes
        TRUE,     // Manual-reset event
        FALSE,    // Initial state is nonsignaled
        L"Global\\Example2_Event" // Same named event
    );
    
    if (hEvent == NULL) {
        std::cerr << "CreateEvent failed with error " << GetLastError() << std::endl;
        return NULL;
    }
    
    DWORD lastError = GetLastError();
    if (lastError == ERROR_ALREADY_EXISTS) {
        std::cout << "Event already exists! Got handle to existing event." << std::endl;
        // Note: The manual-reset parameter is ignored, and we get the auto-reset behavior
    }
    
    return hEvent;
}

// Thread function that expects manual-reset behavior
DWORD WINAPI ManualResetThreadFunc(LPVOID lpParam) {
    HANDLE hEvent = (HANDLE)lpParam;
    int processCount = 0;
    
    while (processCount < 3) {
        std::cout << "Module B thread: Waiting for event signal..." << std::endl;
        DWORD waitResult = WaitForSingleObject(hEvent, INFINITE);
        
        if (waitResult == WAIT_OBJECT_0) {
            processCount++;
            std::cout << "Module B thread: Event signaled! Processing notification #" << processCount << std::endl;
            
            // Simulate some processing time
            Sleep(1000);
            
            // Logic designed for manual-reset event: explicitly reset the event
            // But the event has already been automatically reset
            std::cout << "Module B thread: Explicitly resetting the event (which is already reset)" << std::endl;
            ResetEvent(hEvent);
            
            std::cout << "Module B thread: Processing complete. Waiting for next event..." << std::endl;
        }
        else {
            std::cerr << "Wait failed with error " << GetLastError() << std::endl;
            break;
        }
    }
    
    std::cout << "Module B thread: Processed " << processCount << " notifications. Exiting thread." << std::endl;
    return 0;
}

// Module C thread that also waits on the event
DWORD WINAPI OtherModuleThreadFunc(LPVOID lpParam) {
    HANDLE hEvent = (HANDLE)lpParam;
    int processCount = 0;
    
    while (processCount < 3) {
        std::cout << "Module C thread: Waiting for event signal..." << std::endl;
        DWORD waitResult = WaitForSingleObject(hEvent, INFINITE);
        
        if (waitResult == WAIT_OBJECT_0) {
            processCount++;
            std::cout << "Module C thread: Event signaled! Processing notification #" << processCount << std::endl;
            
            // Simulate some processing time
            Sleep(500); // Less processing time than Module B
            
            std::cout << "Module C thread: Processing complete. Waiting for next event..." << std::endl;
        }
        else {
            std::cerr << "Wait failed with error " << GetLastError() << std::endl;
            break;
        }
    }
    
    std::cout << "Module C thread: Processed " << processCount << " notifications. Exiting thread." << std::endl;
    return 0;
}

int main() {
    std::cout << "Example 2: Manual-Reset Logic in Auto-Reset Event Environment\n" << std::endl;
    
    // First, Module A creates an auto-reset event
    HANDLE hAutoEvent = CreateAutoResetEvent();
    if (hAutoEvent == NULL) {
        return 1;
    }
    
    // Module B tries to create what it thinks is a manual-reset event
    // but actually gets the same auto-reset event
    HANDLE hManualEvent = CreateManualResetEvent(); 
    if (hManualEvent == NULL) {
        CloseHandle(hAutoEvent);
        return 1;
    }
    
    // Module C also opens the same event
    HANDLE hOtherEvent = CreateEvent(NULL, FALSE, FALSE, L"Global\\Example2_Event");
    
    // Create threads
    std::vector<HANDLE> threads;
    
    HANDLE hThread1 = CreateThread(NULL, 0, ManualResetThreadFunc, hManualEvent, 0, NULL);
    if (hThread1 == NULL) {
        std::cerr << "CreateThread failed with error " << GetLastError() << std::endl;
        CloseHandle(hManualEvent);
        CloseHandle(hAutoEvent);
        return 1;
    }
    threads.push_back(hThread1);
    
    HANDLE hThread2 = CreateThread(NULL, 0, OtherModuleThreadFunc, hOtherEvent, 0, NULL);
    if (hThread2 == NULL) {
        std::cerr << "CreateThread failed with error " << GetLastError() << std::endl;
        CloseHandle(hThread1);
        CloseHandle(hManualEvent);
        CloseHandle(hAutoEvent);
        return 1;
    }
    threads.push_back(hThread2);
    
    // Wait for the threads to start
    Sleep(1000);
    
    // Signal the event multiple times
    for (int i = 0; i < 3; i++) {
        std::cout << "\nMain thread: Signaling the event (signal #" << (i+1) << ")..." << std::endl;
        if (!SetEvent(hAutoEvent)) {
            std::cerr << "SetEvent failed with error " << GetLastError() << std::endl;
        }
        
        // Give threads time to process
        Sleep(2000);
    }
    
    // Wait for threads to finish
    WaitForMultipleObjects(static_cast<DWORD>(threads.size()), &threads[0], TRUE, INFINITE);
    
    std::cout << "\nMain: Demo completed. Notice that:" << std::endl;
    std::cout << "     1. Only one thread receives each event signal due to auto-reset behavior" << std::endl;
    std::cout << "     2. Module B thread calls ResetEvent on an already reset event, which has no effect" << std::endl;
    
    // Cleanup
    for (size_t i = 0; i < threads.size(); i++) {
        CloseHandle(threads[i]);
    }
    CloseHandle(hManualEvent);
    CloseHandle(hAutoEvent);
    CloseHandle(hOtherEvent);
    
    std::cout << "\nPress Enter to exit..." << std::endl;
    std::cin.get();
    
    return 0;
}
